package com.xiangyang.service.impl;

import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xiangyang.config.SecurityConfig;
import com.xiangyang.entity.Employee;
import com.xiangyang.entity.LoginUser;
import com.xiangyang.entity.User;
import com.xiangyang.entity.dto.Result;
import com.xiangyang.entity.vo.*;
import com.xiangyang.exception.CustomerAuthenticationException;
import com.xiangyang.exception.VerifyInfoException;
import com.xiangyang.mapper.IEmployeeMapper;
import com.xiangyang.mapper.IUserMapper;
import com.xiangyang.service.IUserService;

import com.xiangyang.utils.CaptchaGenerator;
import com.xiangyang.utils.JwtUtil;
import io.jsonwebtoken.Claims;
import io.lettuce.core.ScriptOutputType;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.util.*;
import java.util.concurrent.TimeUnit;

import static com.xiangyang.utils.DxbCodeUtil.sendCode;
import static com.xiangyang.utils.VerifyInfo.VerifyInfo;

@Service
public class UserServiceImpl extends ServiceImpl<IUserMapper, User> implements IUserService {
    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private IEmployeeMapper iEmployeeMapper;
    @Autowired
    private IUserMapper iUserMapper;
    @Autowired
    private SecurityConfig securityConfig;
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    @Autowired
    private UserDetailsServiceImpl userDetailsService;
    @Value("${config.maxLogin}")
    //自定义的最大登陆次数
    private int maxLogin;


    @Override
    public Result login(String phone, String password) {
        VerifyVo verifyVo = new VerifyVo();
        verifyVo.setPhone(phone);
        verifyVo.setPassword(password);

        //数据正则校验
        try {
            VerifyInfo(verifyVo);
        } catch (VerifyInfoException e) {
            return Result.error(404, e.getMessage());
        }
        //Security进行数据库信息校验
        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(phone, password);
        Authentication authentication = authenticationManager.authenticate(token);
        if (Objects.isNull(authentication))
            Result.error(404, "用户名或密码错误");
        //将查询的用户信息存入上下文中
        SecurityContextHolder.getContext().setAuthentication(authentication);
        //获取用户信息
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        UserDetailInfo userDetailInfo = loginUser.getUserDetailInfo();
        //将用户信息转为json字符串
        JwtJubInfo jwtJubInfo = new JwtJubInfo(userDetailInfo.getUserName(), userDetailInfo.getUserPhone());
        ;
        String accessToken = JwtUtil.CreateToken(JSON.toJSONString(jwtJubInfo), 4 * 7 * 24 * 60 * 60 * 1000L);
        //redis中存储token解决会话失效
        loginStatus(accessToken, userDetailInfo.getUserName());
        //以下是将token返回给controller然后返回给前端
        return Result.ok(accessToken);
    }

    public void loginStatus(String token, String userName) {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        //获取请求者的ip地址
        String userIpAddress = request.getRemoteAddr();
        //登录失效的时间
        long expireTime = 2 * 24 * 60 * 60L;
        //redis中set数据key名为
        String TokenKey = "user:token:" + userName;
        //获取当前用户的所有token
        Set<String> tokens = stringRedisTemplate.opsForSet().members(TokenKey);
        //用户信息不是空
        if (tokens != null) {
            //遍历所有token
            for (String oldToken : tokens) {
                //判断是否是当前用户
                if (userIpAddress.equals(stringRedisTemplate.opsForValue().get(oldToken))) {
                    //删除旧的token
                    stringRedisTemplate.delete(oldToken);
                    //删除旧的token对应的key
                    stringRedisTemplate.opsForSet().remove(TokenKey, oldToken);
                }
            }
        }
        //获取当前用户的所有token的数量
        Long maxLoginSize = stringRedisTemplate.opsForSet().size(TokenKey);
        //最大登录限制且禁止当前用户登陆
        //if (maxLoginSize!=null && maxLoginSize>=maxLogin){
        //throw new InternalAuthenticationServiceException("登录尝试次数超过最大限制");
        //}
        //挤掉之前用户状态
        if (maxLoginSize != null && maxLoginSize >= maxLogin) {
            //随机删除一条Set中的一个值
            String rem = stringRedisTemplate.opsForSet().pop(TokenKey);
            //rem是删除的值为，将redis中的String这个key为rem的值设空字符串
            stringRedisTemplate.opsForValue().set(rem, "");
            //设置redis的key的过期时间,在失效时间内登录会提示被其他地点登录
            redisTemplate.expire(rem, expireTime, TimeUnit.SECONDS);

        }
        //用户登陆成功
        //将当前生成的token存入redis
        stringRedisTemplate.opsForValue().set(token, userIpAddress, expireTime, TimeUnit.SECONDS);
        //将当前token存入这个key中
        stringRedisTemplate.opsForSet().add(TokenKey, token);
        //因为每次登陆新的都会从新设置过期时间所以，当用户全部过期是这个key也会被删除
        stringRedisTemplate.expire(TokenKey, expireTime, TimeUnit.SECONDS);

    }

    @Override
    public Result register(RegisterInfo user) {
        if (!user.getUserPassword().equals(user.getConfirmPassword())) {
            return Result.error(404, "两次密码不一致");
        }
        if (user == null) {
            return Result.error(404, "注册信息不能为空");
        }
        VerifyVo verifyVo = new VerifyVo();
        verifyVo.setPhone(user.getUserPassword());
        verifyVo.setPhone(user.getUserPhone());
        try {
            VerifyInfo(verifyVo);
        } catch (VerifyInfoException e) {
            return Result.error(404, e.getMessage());
        }
        String code = stringRedisTemplate.opsForValue().get(user.getUserPhone());
        if (code != null && code.equals(user.getCode())) {
            LambdaQueryWrapper<User> lambdaQueryWrapperUser = new LambdaQueryWrapper<>();
            lambdaQueryWrapperUser.eq(User::getUserPhone, user.getUserPhone());
            User userInfo = iUserMapper.selectOne(lambdaQueryWrapperUser);
            if (userInfo != null) {
                return Result.error(404, "该手机号已注册");
            }
            //查询员工信息
            LambdaQueryWrapper<Employee> lambdaQueryWrapper = new LambdaQueryWrapper<>();
            lambdaQueryWrapper.eq(Employee::getEmployeeNumber, user.getJobNumber());
            Employee employee = iEmployeeMapper.selectOne(lambdaQueryWrapper);
            if (Objects.isNull(employee))
                return Result.error(404, "工号不存在");
            if (!user.getUserPhone().equals(employee.getPhone()))
                return Result.error(404, "手机号与入职时预留的不一致");
            User insertUser = new User();
            System.out.println(employee.getUserName());
            insertUser.setUserName(employee.getUserName());
            insertUser.setUserPhone(user.getUserPhone());
            insertUser.setUserPassword(securityConfig.passwordEncoder().encode(user.getUserPassword()));
            insertUser.setUserDepartment(employee.getDepartmentId());
            insertUser.setJobNumber(user.getJobNumber());
            if (iUserMapper.insert(insertUser) <= 0)
                return Result.error(404, "注册失败");
            return Result.ok("注册成功");
        } else {
            return Result.error(404, "验证码错误");
        }

    }


    @Override
    public Result logout(HttpServletRequest request, HttpServletResponse response) {
        String token = request.getHeader("token");
        if (Objects.isNull(token) || token.isEmpty()) {
            token = request.getParameter("token");
        }
        if (Objects.isNull(token) || token.isEmpty()) {
            throw new CustomerAuthenticationException("token为空");
        }
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (!Objects.isNull(authentication)) {
            new SecurityContextLogoutHandler().logout(request, response, authentication);
            UserDetailInfo TokenUserinfo = null;
            try {
                Claims cla = JwtUtil.parseJwt(token);
                String TokenUser = cla.getSubject();
                TokenUserinfo = JSON.toJavaObject(TokenUser, UserDetailInfo.class);
                //退出登陆删除String的key和Set中的值
                stringRedisTemplate.delete(token);
                stringRedisTemplate.opsForSet().remove("user:token:" + TokenUserinfo.getUserName(), token);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }

        }

        return Result.ok("退出成功");
    }

    @Override
    public Result forgetPassword(ForgetPwdInfo forgetPwdInfo) {
        VerifyVo verifyVo = new VerifyVo();
        verifyVo.setPhone(forgetPwdInfo.getPhone());
        verifyVo.setPassword(forgetPwdInfo.getPassword());
        verifyVo.setCode(forgetPwdInfo.getCode());
        try {
            VerifyInfo(verifyVo);
        } catch (VerifyInfoException e) {
            return Result.error(404, e.getMessage());
        }
        String code = stringRedisTemplate.opsForValue().get(forgetPwdInfo.getPhone());
        if (code != null && code.equals(forgetPwdInfo.getCode())) {
            if (!forgetPwdInfo.getPassword().equals(forgetPwdInfo.getConfirmPassword())) {
                return Result.error(404, "两次密码不一致");
            }

            LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
            lambdaQueryWrapper.eq(User::getUserPhone, forgetPwdInfo.getPhone());
            User user = iUserMapper.selectOne(lambdaQueryWrapper);
            if (Objects.isNull(user)) {
                return Result.error(404, "用户不存在");
            }
            user.setUserPassword(securityConfig.passwordEncoder().encode(forgetPwdInfo.getPassword()));
            int i = iUserMapper.updateById(user);
            return i > 0 ? Result.ok() : Result.error(500, "修改失败");
        } else {
            return Result.error(404, "验证码错误");
        }
    }

    @Override
    public Result getCode(String phone) {

        VerifyVo verifyVo = new VerifyVo();
        verifyVo.setPhone(phone);
        try {
            VerifyInfo(verifyVo);
        } catch (VerifyInfoException e) {
            return Result.error(404, e.getMessage());
        }
        //查询员工信息
        LambdaQueryWrapper<Employee> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(Employee::getPhone, phone);
        Employee employee = iEmployeeMapper.selectOne(lambdaQueryWrapper);
        if (Objects.isNull(employee))
            return Result.error(404, "手机号与入职时预留的不一致");
        CaptchaGenerator generator = new CaptchaGenerator();
        String Code = generator.generateNumericCaptcha(6);
        //通过Security中的loadUserByUsername先获取到数据库中用户的信息
        sendCode(phone, Code);
        stringRedisTemplate.opsForValue().set(phone, Code, 5, TimeUnit.MINUTES);
        return Result.ok("忘记密码");
    }

    @Override
    public Result changePwd(ChagePwdInfo chagePwdInfo) {
        try {
            VerifyVo verifyVo = new VerifyVo();
            verifyVo.setPassword(chagePwdInfo.getNewPassword());
            try {
                VerifyInfo(verifyVo);
            } catch (VerifyInfoException e) {
                return Result.error(404, e.getMessage());
            }
            if (!chagePwdInfo.getNewPassword().equals(chagePwdInfo.getConfirmPassword())) {
                return Result.error(404, "两次密码不一致");
            }
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
            LoginUser userDetailInfo = (LoginUser) authentication.getPrincipal();
            LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
            lambdaQueryWrapper.eq(User::getUserPhone, userDetailInfo.getUserDetailInfo().getUserPhone());
            User user = iUserMapper.selectOne(lambdaQueryWrapper);
            // securityConfig.passwordEncoder() 返回的是 BCryptPasswordEncoder 实例
            // 判断旧密码是否正确
            PasswordEncoder passwordEncoder = securityConfig.passwordEncoder();
            if (passwordEncoder.matches(chagePwdInfo.getNewPassword(), user.getUserPassword()))
                return Result.error(404, "新旧密码一致");
            if (passwordEncoder.matches(chagePwdInfo.getOldPassword(), user.getUserPassword())) {
                user.setUserPassword(passwordEncoder.encode(chagePwdInfo.getNewPassword()));
                iUserMapper.updateById(user);
                return Result.ok("修改成功");
            } else {
                return Result.error(404, "旧密码错误");
            }


        } catch (NullPointerException e) {
            return Result.error(404, "请输入正确的参数");
        }


    }
}
